/**
 * 
 */
package info.niwota.webagent.peer;

import info.niwota.org.json.JSONObject;
import info.niwota.webagent.util.BlockingMap;

import com.google.appengine.api.xmpp.JID;
import com.google.appengine.api.xmpp.Message;
import com.google.appengine.api.xmpp.MessageBuilder;
import com.google.appengine.api.xmpp.MessageType;
import com.google.appengine.api.xmpp.SendResponse;
import com.google.appengine.api.xmpp.SendResponse.Status;
import com.google.appengine.api.xmpp.XMPPService;
import com.google.appengine.api.xmpp.XMPPServiceFactory;

/**
 * @author qiangli
 * 
 */
public class Messenger {
	public static final JID APP_JID = new JID("hotwebproxy@appspot.com");

	private static long seq = System.currentTimeMillis();

	private static final Messenger instance = new Messenger();

	private static final BlockingMap<String, NoteWrapper> queues = new BlockingMap<String, NoteWrapper>();

	private static final XMPPService xmpp = XMPPServiceFactory.getXMPPService();

	private static final long TIMEOUT_DEFAULT = 60; // 1 min

	class NoteWrapper {
		public String peer;
		public String message;

		public long ts = System.currentTimeMillis();
	}

	private Messenger() {
	}

	synchronized private static String nextId(String prefix) {
		return "agent-tid-" + prefix + (++seq);
	}

	public static Messenger getInstance() {
		return instance;
	}

	public void receive(String peer, String body) {
		String tid = null;
		try {
			JSONObject json = new JSONObject(body);
			tid = json.getString("tid");
			String msg = json.getString("message");
			NoteWrapper wrap = queues.peek(tid);
			
			if (wrap != null && wrap.peer.equalsIgnoreCase(peer)) {
				wrap.message = msg;
			} else {
				//discard
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (tid != null) {
				queues.release(tid);
			}
		}
	}

	public WebAddress getWebAddress(String from, String peer) throws Exception {
		return getWebAddress(from, peer, TIMEOUT_DEFAULT);
	}
	
	public WebAddress getWebAddress(String from, String peer, long timeout)
			throws Exception {
		JSONObject json = new JSONObject();
		json.put("cmd", "address");
		json.put("jid", from);
		json.put("peer", peer);

		String req = "JSONP\r\n" + json.toString();

		Note n = send(from, peer, req, timeout);
		
		if (n.rc == Note.RC_SUCCESS) {
			JSONObject res =  new JSONObject(n.message);
			WebAddress addr = new WebAddress();
			addr.ip = res.getString("ip");
			addr.port = res.getInt("port");
			return addr;
		}
		
		throw new Exception(Note.errorMessage(n.rc));
	}

	public Note send(String from, String peer, String message, long timeout)
			throws Exception {
		String key = nextId(from + "|" + peer);

		NoteWrapper v = new NoteWrapper();
		v.peer = peer;
		
		JID jid = new JID(peer);

		Message msg = new MessageBuilder().withMessageType(MessageType.NORMAL)
				.withFromJid(APP_JID).withRecipientJids(jid).withBody(message)
				.build();

		Note note = new Note();
		note.tid = key;

		if (xmpp.getPresence(jid).isAvailable()) {
			SendResponse res = xmpp.sendMessage(msg);
			Status status = res.getStatusMap().get(jid);
			switch (status) {
			case SUCCESS:
				queues.put(key, v);
				NoteWrapper wrap = queues.get(key, timeout);
				if (wrap != null) {
					note.message = wrap.message;
					note.rc = Note.RC_SUCCESS;
				} else {
					note.rc = Note.RC_ERROR;
				}
				break;
			case INVALID_ID:
				note.rc = Note.RC_INVALID_ID;
				break;
			case OTHER_ERROR:
				note.rc = Note.RC_ERROR;
				break;
			default:
				note.rc = Note.RC_ERROR;
			}
		} else {
			note.rc = Note.RC_UNAVAILABLE;
		}
		
		return note;
	}

}
